#pragma once

#include <iostream>
#include <vector>
#include <unordered_map>
#include <algorithm>
#include <sstream>
#include <unistd.h>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include <fcntl.h>
#include <wait.h>
#include "Log.hpp"
#include "Util.hpp"

#define SEP ": "
#define WEB_ROOT "wwwroot"
#define HOME_PAGE "index.html"
#define HTTP_VERSION "HTTP/1.0"
#define LINE_END "\r\n"
#define PAGE_404 "404.html"

#define OK 200
#define BAD_REQUEST 400
#define NOT_FOUND 404
#define SERVER_ERROR 500

static std::string Code2Desc(int code)
{
    std::string desc;
    switch (code)
    {
    case 200:
        desc = "OK";
        break;
    case 404:
        desc = "Not Found";
        break;
    default:
        break;
    }
    return desc;
}
static std::string Suffix2Desc(const std::string& suffix) // 将相应的后缀转换成相应的文件类型
{
    static std::unordered_map<std::string, std::string> suffix2desc =
    {
        {".html", "text/html"},
        {".css", "text/css"},
        {".js", "application/javascript"},
        {".jpg", "application/x-jpg"},
        {".xml", "application/xml"},
    };

    auto iter = suffix2desc.find(suffix);
    if (iter != suffix2desc.end())
    {
        return iter->second;
    }
    return "text/html";
}

class HttpRequest // HTTP请求
{
public:
    // HTTP请求内容
    std::string _request_line; // 请求行
    std::vector<std::string> _request_header; // 请求报头
    std::string _blank; // 空行
    std::string _request_body; // 请求正文

    // 请求解析后的结果
    std::string _method; // 请求资源
    std::string _uri; // 请求方法，路径+正文，path?args
    std::string _version; // 版本号

    std::unordered_map<std::string, std::string> _header_kv; // 解析结果键值对
    int _content_length; // 正文长度
    std::string _path; // 请求的资源
    std::string _suffix; // 后缀
    std::string _query_string; // uri中的参数，path?args的args

    bool _cgi; // 是否需要使用CGI模式
    int _size;
public:
    HttpRequest()
        : _content_length(0)
        , _cgi(false)
    {}
    ~HttpRequest()
    {}
};

class HttpResponse // HTTP响应
{
public:
    std::string _status_line; // 状态行
    std::vector<std::string> _response_header; // 响应报头
    std::string _blank; // 空行
    std::string _response_body; // 响应正文

    int _status_code; // 状态码
    int _fd;

public:
    HttpResponse()
        : _blank(LINE_END)
        , _status_code(OK)
        , _fd(-1)
    {}
    ~HttpResponse()
    {}
};

class EndPoint // IO通信，读取请求，分析请求，构建响应
{
private:
    int _sock;
    HttpRequest _http_request;
    HttpResponse _http_response;

public:
    EndPoint(int sock)
        : _sock(sock)
    {}
    ~EndPoint()
    {
        close(_sock);
    }

    void RecvHttpRequest()
    {
        RecvHttpRequestLine();    // 读取请求行
        RecvHttpRequestHeader();  // 读取请求报头
        ParseHttpRequestLine();   // 解析请求行
        ParseHttpRequestHeader(); // 解析请求报头
        RecvHttpRequestBody();    // 读取请求正文
    }

    void BuildHttpResponse() // 请求全部读完，构建响应
    {
        std::string path;
        struct stat st;
        std::size_t found = 0;
        auto& code = _http_response._status_code;
        if (_http_request._method != "GET" && _http_request._method != "POST") // 非法请求
        {
            std::cout << "method: " << _http_request._method << std::endl;
            LOG(WARNING, "method is not right");
            code = BAD_REQUEST;
            goto END;
        }
        if (_http_request._method == "GET") // 目前只有GET需要判断是否带参
        {
            size_t pos = _http_request._uri.find('?'); // 找问号
            if (pos != std::string::npos)
            {   // 把uri切分成path和query_string，以问号为分隔符
                Util::CutString(_http_request._uri, _http_request._path, _http_request._query_string, "?");
                _http_request._cgi = true;
            }
            else
            {
                _http_request._path = _http_request._uri;
            }
        }
        else if (_http_request._method == "POST")
        {
            _http_request._path = _http_request._uri;
            _http_request._cgi = true;
        }
        else
        {
            // 什么都不处理，待拓展
        }
        path = _http_request._path;
        _http_request._path = WEB_ROOT;
        _http_request._path += path;
        if (_http_request._path[_http_request._path.size() - 1] == '/')
        {   // 如果请求是根目录，加上默认首页
            _http_request._path += HOME_PAGE;
        }
        if (stat(_http_request._path.c_str(), &st) == 0) // 资源存在
        {
            if (S_ISDIR(st.st_mode)) // 请求的资源是一个目录，做一下相关处理
            {
                _http_request._path += "/"; // 目录不会以/结尾
                _http_request._path += HOME_PAGE;
                stat(_http_request._path.c_str(), &st);
            }
            if ((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH))
            {   // 如果任何一个人有可执行程序的权限，特殊处理
                _http_request._cgi = true;
            }
            _http_request._size = st.st_size;
        }
        else // 资源不存在
        {
            LOG(WARNING, _http_request._path + " Not Found");
            code = NOT_FOUND;
            goto END;
        }

        found = _http_request._path.rfind("."); // 找后缀，倒着找点
        if (found == std::string::npos)
        {
            _http_request._suffix = ".html";
        }
        else
        {
            _http_request._suffix = _http_request._path.substr(found);
        }

        if (_http_request._cgi)
        {
            code = ProcessCgi(); // 执行目标程序
        }
        else
        {
            code = ProcessNonCgi(_http_request._size); // 返回网页，只需打开，在下面END构建HTTP响应
        }
    END:
        // std::cout << "debug uri: " << _http_request._uri << std::endl;
        // std::cout << "debug path: " << _http_request._path << std::endl;
        // std::cout << "debug query_string: " << _http_request._query_string << std::endl;
        BuildHttpResponseHelper(); // 状态行填充了，响应报头、空行、正文都有了
    }

    void SendHttpResponse() // 发送响应
    {
        send(_sock, _http_response._status_line.c_str(), _http_response._status_line.size(), 0);
        for (auto iter : _http_response._response_header)
        {
            send(_sock, iter.c_str(), iter.size(), 0);
        }
        send(_sock, _http_response._blank.c_str(), _http_response._blank.size(), 0);
        if (_http_request._cgi)
        {
            auto& response_body = _http_response._response_body;
            size_t size = 0;
            size_t total = 0;
            const char* start = response_body.c_str();
            while (total < response_body.size() &&
                    (size = send(_sock, start + total, response_body.size() - total, 0)) > 0)
            {
                total += size;
            }
        }
        else
        {
            sendfile(_sock, _http_response._fd, nullptr, _http_request._size);
            close(_http_response._fd);
        }
    }

private:
    void RecvHttpRequestLine() // 读取请求行
    {
        auto& line = _http_request._request_line;
        Util::ReadLine(_sock, line);
        line.resize(line.size() - 1); // 去掉换行
        LOG(INFO, _http_request._request_line);
    }
    void RecvHttpRequestHeader() // 读取请求报头
    {
        std::string line;
        while (true)
        {
            line.clear();
            Util::ReadLine(_sock, line);
            if (line == "\n")
            {
                _http_request._blank = line;
                break;
            }
            line.resize(line.size() - 1);
            _http_request._request_header.push_back(line);
            // LOG(INFO, line);
        }
    }
    void ParseHttpRequestLine() // 解析请求行
    {
        auto& line = _http_request._request_line;
        std::stringstream ss(line); // 以空格分割
        ss >> _http_request._method >> _http_request._uri >> _http_request._version;

        auto& method = _http_request._method; // 转大写
        std::transform(method.begin(), method.end(), method.begin(), ::toupper);
    }
    void ParseHttpRequestHeader() // 解析请求报头
    {
        for (auto str : _http_request._request_header)
        {
            std::string str1, str2;
            if (Util::CutString(str, str1, str2, SEP))
            {
                _http_request._header_kv.insert(make_pair(str1, str2));
            }
        }
    }

    bool IsNeedRecvHttpRequestBody() // 确定是否需要读取请求正文
    {
        auto& method = _http_request._method;
        if (method == "POST")
        {
            auto& header_kv = _http_request._header_kv;
            auto iter = header_kv.find("Content-Length"); // 通过Content-Length获取请求正文长度
            if (iter != header_kv.end())
            {
                LOG(INFO, "Post Method, Content-Length: " + iter->second);
                _http_request._content_length = atoi(iter->second.c_str());
                return true;
            }
        }
        return false;
    }
    void RecvHttpRequestBody() // 读取请求正文
    {
        if (IsNeedRecvHttpRequestBody())
        {
            int content_length = _http_request._content_length;
            auto& body = _http_request._request_body;

            char ch = 0;
            while (content_length)
            {
                ssize_t s = recv(_sock, &ch, 1, 0);
                if (s > 0)
                {
                    body.push_back(ch);
                    content_length--;
                }
                else
                {
                    break;
                }
            }
            LOG(INFO, body);
        }
    }

    int ProcessNonCgi(int size)
    {
        _http_response._fd = open(_http_request._path.c_str(), O_RDONLY);
        if (_http_response._fd > 0)
        {
            return OK;
        }
        return NOT_FOUND;
    }

    int ProcessCgi()
    {
        LOG(INFO, "process cgi mthod");
        int code = OK;
        // 父进程数据
        auto& method = _http_request._method;
        auto& query_string = _http_request._query_string; // GET
        auto& body_text = _http_request._request_body; // POST
        auto& bin = _http_request._path; // 一定存在让子进程执行的目标程序
        int content_length = _http_request._content_length;
        auto& response_body = _http_response._response_body;

        std::string query_string_env;
        std::string method_env;
        std::string content_length_env;

        // 父进程角度
        int input[2];
        int output[2];
        if (pipe(input) < 0)
        {
            LOG(ERROR, "pipe input error");
            code = SERVER_ERROR;
            return code;
        }
        if (pipe(output) < 0)
        {
            LOG(ERROR, "pipe output error");
            code = SERVER_ERROR;
            return code;
        }

        pid_t pid = fork();
        if (pid == 0) // 子进程
        {
            close(input[0]);
            close(output[1]);

            method_env = "METHOD=";
            method_env += method;

            putenv((char*)method_env.c_str());

            if (method == "GET")
            {
                query_string_env = "QUERY_STRING=";
                query_string_env += query_string;
                putenv((char*)query_string_env.c_str());
                LOG(INFO, "Get Method, Add Query_String Env");
            }
            else if (method == "POST")
            {
                content_length_env = "CONTENT_LENGTH=";
                content_length_env += std::to_string(content_length);
                putenv((char*)content_length_env.c_str());
                LOG(INFO, "Post Method, Add Content_Length Env");
            }
            else
            {
                // 什么都不处理，待拓展
            }

            // 子进程角度
            // input[1]: 写出
            // output[0]: 读入
            std::cout << "bin: " << bin << std::endl;
            dup2(output[0], 0);
            dup2(input[1], 1);

            execl(bin.c_str(), bin.c_str(), nullptr);
            exit(1);
        }
        else if (pid < 0)
        {
            LOG(ERROR, "fork error");
            return 404;
        }
        else // 父进程
        {
            close(input[1]);
            close(output[0]);

            if (method == "POST")
            {
                const char* start = body_text.c_str();
                int total = 0;
                int size = 0;
                while (total < content_length && 
                        (size = write(output[1], start + total, body_text.size() - total)) > 0)
                {
                    total += size;
                }
            }

            char ch = 0;
            while (read(input[0], &ch, 1) > 0)
            {
                response_body.push_back(ch);
            }
            int status = 0;
            pid_t ret = waitpid(pid, &status, 0);
            if (ret == pid)
            {
                if (WIFEXITED(status))
                {
                    if (WEXITSTATUS(status) == 0)
                    {
                        code = OK;
                    }
                    else
                    {
                        code = BAD_REQUEST;
                    }
                }
                else
                {
                    code = SERVER_ERROR;
                }
            }
            close(input[0]);
            close(output[1]);
        }
        return code;
    }

    void BuildHttpResponseHelper() // 构建响应报头->处理出错
    {
        // http_request;
        // http_response;
        auto& code = _http_response._status_code;
        // 构建状态行
        auto& status_line = _http_response._status_line;
        status_line += HTTP_VERSION;
        status_line += " ";
        status_line += std::to_string(code);
        status_line += " ";
        status_line += Code2Desc(code);
        status_line += LINE_END;

        // 构建响应正文,可能包括响应报头
        std::string path = WEB_ROOT;
        path += "/";
        switch (code)
        {
        case OK:
            BuildOkResponse();
            break;
        case NOT_FOUND:
            path += PAGE_404;
            HandlerError(path);
            break;
        case BAD_REQUEST:
            // 先统一404，待拓展
            path += PAGE_404;
            HandlerError(path);
            break;
        case SERVER_ERROR:
            // 先统一404，待拓展
            path += PAGE_404;
            HandlerError(path);
            break;
            // case 500:
            //     HandlerError(PAGE_500);
            //     break;
        default:
            break;
        }
    }

    void BuildOkResponse()
    {
        std::string line = "Content-Type: ";
        line += Suffix2Desc(_http_request._suffix);
        line += LINE_END;
        _http_response._response_header.push_back(line);

        line = "Content-Length: ";
        if (_http_request._cgi)
        {
            line += std::to_string(_http_response._response_body.size());
        }
        else
        {
            line += std::to_string(_http_request._size); // Get
        }
        line += LINE_END;
        _http_response._response_header.push_back(line);
    }
    void HandlerError(std::string page)
    {
        std::cout << "debug: " << page << std::endl;
        _http_request._cgi = false;
        _http_response._fd = open(page.c_str(), O_RDONLY); // 打开传入的页面
        if (_http_response._fd > 0)
        {
            struct stat st;
            stat(page.c_str(), &st);
            _http_request._size = st.st_size;

            std::string line = "Content-Type: text/html";
            line += LINE_END;
            _http_response._response_header.push_back(line);

            line = "Content-Length: ";
            line += std::to_string(st.st_size);
            line += LINE_END;
            _http_response._response_header.push_back(line);
        }
    }
};

// #define DEBUG
class Entrance // 线程入口
{
public:
    Entrance()
    {}
    ~Entrance()
    {}
    static void* HandlerRequest(void* _sock)
    {
        int sock = *(int*)_sock; // 临时测试方案
        LOG(INFO, "Hander Request Begin");
#ifdef DEBUG
        // For Test
        char buffer[4096];
        recv(sock, buffer, sizeof(buffer), 0);
        std::cout << "-------------begin----------------" << std::endl;
        std::cout << buffer << std::endl;
        std::cout << "-------------end----------------" << std::endl;
#else
        EndPoint* ep = new EndPoint(sock);
        ep->RecvHttpRequest();
        ep->BuildHttpResponse();
        ep->SendHttpResponse();
        delete ep;
#endif
        close(sock);
        LOG(INFO, "Hander Request End");
        return nullptr;
    }
};